// ....................................................................................................................
//	Author: Copyright (C) 2012 Ing. Marek Hubal
//	Email: hubal@acasoftware.org
// ....................................................................................................................

#define WIN32_LEAN_AND_MEAN 
#pragma warning (disable: 4996)

#include <Windows.h>
#include "resource.h"
#include <Msi.h>
#include <Shellapi.h>

HWND SplashWindow; // Handle splash okna
HINSTANCE AppInstance; // Instancia aplikacie
HBITMAP SplashImage; // Splash obrazok
HMODULE LoaderModuleHandle; // Handle kniznice "loader.dll"

SYSTEMTIME AppStartTime; // Zaciatok zobrazenia splash obrazovky

#define SPLASH_WINDOW_WIDTH 640
#define SPLASH_WINDOW_HEIGHT 120

// Prototypy funkcii 
typedef int (*PInitApplication)(HMODULE pModuleHandle, HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow);
typedef int (*PRunApplication)(void);
typedef void (*PCloseApplication)(void);

#ifdef X64
#define TEXT_1 "Before start totalem a Microsoft Visual C++ 2010 Redistributable Package (x64) will be installed automatically. Please wait a while."

void __fastcall InstallPrereq64(void);
#else
#define TEXT_1 "Before start totalem a Microsoft Visual C++ 2010 Redistributable Package (x32) will be installed automatically. Please wait a while."

void __fastcall InstallPrereq32(void);
#endif

// ....................................................................................................................
//	Ziskanie poctu uplynutych milisekund od zaciatku nacitavania
//	Vstup:
//			ziaden
//	Vystup:
//			pocet uplynutych sekund od AppStartTime
// ....................................................................................................................
int GetElapsedMiliseconds(void) {
	SYSTEMTIME ST; ::GetSystemTime(&ST);
	FILETIME F1, F2; ::SystemTimeToFileTime(&AppStartTime, &F1); ::SystemTimeToFileTime(&ST, &F2);
	ULARGE_INTEGER U1, U2; U1.HighPart = F1.dwHighDateTime; U1.LowPart = F1.dwLowDateTime; U2.HighPart = F2.dwHighDateTime; U2.LowPart = F2.dwLowDateTime;
	U2.QuadPart -= U1.QuadPart;
	return (int)(U2.QuadPart / 10000); // Mam milisekundy
}
// ....................................................................................................................

// ....................................................................................................................
//	Window procedure pre SplashWindow
//	Vstup:
//			hwnd - handle okna
//			uMsg - sprava
//			wParam, lParam - parametre spravy
//	Vystup:
//			podla spracovania
// ....................................................................................................................
LRESULT CALLBACK SplashScreenWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	switch (uMsg) {
		case WM_ERASEBKGND: {
			BITMAP bm;
			HDC DC = (HDC)wParam; // Ziskam content
			
			HDC hdcMem = ::CreateCompatibleDC(DC);
			HBITMAP hbmOld = (HBITMAP)::SelectObject(hdcMem, SplashImage);

			::GetObject(SplashImage, sizeof(bm), &bm); // Ziskam rozmery obrazku

			::BitBlt(DC, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); // Zobrazim

			::SelectObject(hdcMem, hbmOld); // Zrusim
			::DeleteDC(hdcMem);
			return 0;
							}
	}
	return ::DefWindowProc(hwnd, uMsg, wParam, lParam); // Volam default proceduru
}
// ....................................................................................................................


// ....................................................................................................................
//	Zobrazenie okna s logom pri spustani
//	Vstup:
//			ziaden
//	Vystup:
//			true / false
// ....................................................................................................................
bool __fastcall ShowSplashScreen(void) {

	WNDCLASS WC;
	WC.cbClsExtra = WC.cbWndExtra = 0;
	WC.hbrBackground = NULL; WC.hCursor = ::LoadCursor(NULL, IDC_ARROW); WC.hIcon = ::LoadIcon(AppInstance, MAKEINTRESOURCE(103));
	WC.hInstance = AppInstance; WC.lpfnWndProc = SplashScreenWindowProc; WC.lpszClassName = "totalem_class_name"; WC.lpszMenuName = NULL;
	WC.style = CS_VREDRAW | CS_HREDRAW | CS_NOCLOSE;
	::RegisterClass(&WC); // Registrujem triedu aplikacie

	SIZE DesktopS; DesktopS.cx = ::GetSystemMetrics(SM_CXSCREEN); DesktopS.cy = ::GetSystemMetrics(SM_CYSCREEN); 
	DesktopS.cx -= SPLASH_WINDOW_WIDTH; DesktopS.cx /= 2; DesktopS.cy -= SPLASH_WINDOW_HEIGHT; DesktopS.cy /= 2; // Urcim poziciu vycentrovaneho okna

	SplashImage = (HBITMAP)::LoadImage(AppInstance, MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP, 0, 0, LR_VGACOLOR); // Nacitam obrazok
	if (SplashImage == NULL) return false;

	SplashWindow = ::CreateWindowEx(WS_EX_TOPMOST, "totalem_class_name", "", WS_POPUP | WS_VISIBLE, DesktopS.cx, DesktopS.cy, SPLASH_WINDOW_WIDTH, SPLASH_WINDOW_HEIGHT, NULL, NULL, AppInstance, 0);
	if (SplashWindow == NULL) {
		::DeleteObject(SplashImage); // Zrusim obrazok
		return false; // Chyba vytvorenie okna?
	}

	::UpdateWindow(SplashWindow);
	::ShowWindow(SplashWindow, SW_NORMAL);

	return true; // Vsetko OK
}
// ....................................................................................................................

// ....................................................................................................................
//	Uzatvorenie okna s logom
//	Vstup:
//			ziaden
//	Vystup:
//			ziaden
// ....................................................................................................................
void __fastcall CloseSplashScreen(void) {
	::DestroyWindow(SplashWindow); // Zrusenie okna
}
// ....................................................................................................................

// ....................................................................................................................
//	Hlavna funkcia aplikacie
//	Vstup:
//			hInstance - instancia aplikacie
//			hPrevInstance - nepouziva sa
//			lpCmdLine - parametre spustenia
//			nCmdShow - sposob zobrazenie hlavneh okna aplikacie
//	Vystup:
//			podla spracovania
// ....................................................................................................................
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
	
	AppInstance = hInstance;

#ifdef X64
	bool InstallPrereq = false;
	int R = ::MsiQueryProductState("{DA5E371C-6333-3D8A-93A4-6FD5B20BCC6E}"); // VC 2010 
	if (R == INSTALLSTATE_DEFAULT) InstallPrereq |= true;
	R = ::MsiQueryProductState("{1D8E6291-B0D5-35EC-8441-6616F567A0F7}"); // VC 2010 SP1
	if (R == INSTALLSTATE_DEFAULT) InstallPrereq |= true;
	if (!InstallPrereq) InstallPrereq64();
#else
	bool InstallPrereq = false;
	int R = ::MsiQueryProductState("{196BB40D-1578-3D01-B289-BEFC77A11A1E}"); // VC 2010 
	if (R == INSTALLSTATE_DEFAULT) InstallPrereq |= true;
	R = ::MsiQueryProductState("{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5}"); // VC 2010 SP1
	if (R == INSTALLSTATE_DEFAULT) InstallPrereq |= true;
	if (!InstallPrereq) InstallPrereq32();
#endif

	if (!ShowSplashScreen()) return -1; // Zobrazim splash screen
	
	::GetSystemTime(&AppStartTime); // Ulozim zaciatok nacitavania kniznic

	// Nacitavanie kniznic a inacializacia aplikacie

	LoaderModuleHandle = ::LoadLibrary("loader.dll");
	if (LoaderModuleHandle == NULL) {
		CloseSplashScreen(); // Uzatvorim okno s logom
		return -2;
	}

	// Ziskam smerniky na exportovane funkcie
	PInitApplication InitApplication = (PInitApplication)::GetProcAddress(LoaderModuleHandle, "InitApplication");
	PRunApplication RunApplication = (PRunApplication)::GetProcAddress(LoaderModuleHandle, "RunApplication");
	PCloseApplication CloseApplication = (PCloseApplication)::GetProcAddress(LoaderModuleHandle, "CloseApplication");

	if ((InitApplication == NULL) || (RunApplication == NULL) || (CloseApplication == NULL)) { // Chyba?
		::FreeLibrary(LoaderModuleHandle); // Uvolnim kniznicu
		CloseSplashScreen(); // Uzatvorim okno s logom
		return -3;
	}

	R = InitApplication(LoaderModuleHandle, AppInstance, lpCmdLine, nCmdShow); // Inicializujem aplikaciu
	if (R != 0) {
		if (R == 1) { // Ukoncenie multi instancie?
			CloseApplication(); // Uzatvorim aplikaciu
		}
		::FreeLibrary(LoaderModuleHandle); // Uvolnim kniznicu
		CloseSplashScreen(); // Uzatvorim okno s logom
		return -3;
	}

	// Koniec nacitavania a inicializacie

	while(true) { // Pauza na zobrazenie splash obrazovky
		if (GetElapsedMiliseconds() > 1500) break;
		::Sleep(100);
	}

	CloseSplashScreen(); // Uzatvorim okno s logom

	R = RunApplication(); // Spustim aplikaciu

	CloseApplication(); // Uzatvorim aplikaciu
	::FreeLibrary(LoaderModuleHandle); // Uvolnim kniznicu
	return 0;
}
// ....................................................................................................................

// ....................................................................................................................
// Instalacia potrebnych balickov pre prostredie 32 bit
//	Vstup:
//			ziaden
//	Vystup:
//			ziaden
// ....................................................................................................................
void __fastcall InstallPrereq32(void) {
	char BUF[1024];

	::MessageBox(NULL, TEXT_1, "totalem", MB_OK + MB_ICONINFORMATION);

	::GetModuleFileName(AppInstance, BUF, 1023);
	char* P = strrchr(BUF, '\\');
	if (P == NULL) return; // Nejaka chyba?
	*P = 0; strcat(BUF, "\\Prereq\\vcredist_x86.exe"); // Vytvorim cestu a nazov ku suboru

	SHELLEXECUTEINFO sei; 
     ZeroMemory (&sei, sizeof (SHELLEXECUTEINFO)); 
     sei.cbSize = sizeof (SHELLEXECUTEINFO); 
     sei.lpVerb = "open";
     sei.lpFile = BUF;
     sei.lpParameters = "/passive /norestart";
     sei.nShow = SW_HIDE;
     sei.fMask = SEE_MASK_NOCLOSEPROCESS;

     ShellExecuteEx(&sei); 
     WaitForSingleObject(sei.hProcess, INFINITE);
}
// ....................................................................................................................

// ....................................................................................................................
// Instalacia potrebnych balickov pre prostredie 64 bit
//	Vstup:
//			ziaden
//	Vystup:
//			ziaden
// ....................................................................................................................
void __fastcall InstallPrereq64(void) {
	char BUF[1024];

	::MessageBox(NULL, TEXT_1, "totalem", MB_OK + MB_ICONINFORMATION);

	::GetModuleFileName(AppInstance, BUF, 1023);
	char* P = strrchr(BUF, '\\');
	if (P == NULL) return; // Nejaka chyba?
	*P = 0; strcat(BUF, "\\Prereq\\vcredist_x64.exe"); // Vytvorim cestu a nazov ku suboru

	SHELLEXECUTEINFO sei; 
     ZeroMemory (&sei, sizeof (SHELLEXECUTEINFO)); 
     sei.cbSize = sizeof (SHELLEXECUTEINFO); 
     sei.lpVerb = "open";
     sei.lpFile = BUF;
     sei.lpParameters = "/passive /norestart";
     sei.nShow = SW_HIDE;
     sei.fMask = SEE_MASK_NOCLOSEPROCESS;

     ShellExecuteEx(&sei); 
     WaitForSingleObject(sei.hProcess, INFINITE);
}
// ....................................................................................................................
